<?php
namespace base\container;

use base\ServiceContainer;
use think\facade\Db;

/**
 * 服务逻辑流构建
 */
class ServiceLogic
{
    /**
     * 服务容器
     * @var ServiceContainer
     */
    private $container;

    private $logicWidget = [];

    private $namespace = 'app\\widget';

    /**
     * 构造函数
     * @param ServiceContainer $container
     */
    public function __construct(ServiceContainer $container)
    {
        $this->container = $container;
        $this->setLogicWidget();
    }

    /**
     * 构建逻辑插件
     *
     * @return void
     */
    private function setLogicWidget()
    {
        $method = $this->container->method;
        
        $logicFlow = Db::table('ykfl_logic_conf')->where(['method_id'=>$method['service_method_id']])->find();

        if(!$logicFlow)return true;

        $process = Db::table('ykfl_logic_process')->where(['logic_conf_id'=>$logicFlow['logic_conf_id']])->order('position_type ASC,position ASC')->select();

        $service = $process->where('action_type','=','method')->toArray();
        $methodArr = Db::table('ykfac_service_method')->alias('sm')
                        ->whereIn('service_method_id',array_column($service,'action_id'))
                        ->leftJoin(['ykfac_service'=>'s'],'s.service_id=sm.service_id')
                        ->field('sm.*,s.alias')
                        ->select()->toArray();
        $methodArr = array_combine(array_column($methodArr,'service_method_id'),$methodArr);

        $widget = $process->where('action_type','=','widget')->toArray();
        $widgetArr = Db::table('ykfac_widget')->alias('w')
                        ->whereIn('w.widget_id',array_column($widget,'action_id'))
                        ->leftJoin(['ykfac_widget_class'=>'wcl'],'w.widget_class_id=wcl.widget_class_id')
                        ->field('w.*,wcl.class_name as widget_class')
                        ->select()
                        ->toArray();
        $widgetArr = array_combine(array_column($widgetArr,'widget_id'),$widgetArr);

        $logicWidget = [];

        foreach ($process->toArray() as $value) {
            switch ($value['action_type']) {
                case 'method':
                    $methodInfo = $methodArr[$value['action_type']]??null;
                    if($methodInfo){
                        $temp = [
                            'type'=>'method',
                            'service'=>$methodInfo['alias'],
                            'method'=>$methodInfo['method_name'],
                        ];
                    }
                    break;
                case 'widget':
                    $widgetInfo = $widgetArr[$value['action_id']]??null;
                    if($widgetInfo){
                        $namespace = empty($widgetInfo['widget_namespace'])?$this->namespace:$widgetInfo['widget_namespace'];
                        $temp = [
                            'type'=>'widget',
                            'class'=>$namespace.'\\'.ucfirst($widgetInfo['widget_class']),
                            'method'=>$widgetInfo['method']??'handle',
                        ];
                    }
                    break;
                default:
                    
                    break;
            }
            if(empty($temp))continue;

            $temp['condition']=$value['condition'];
            $logicWidget[$value['position_type']][]=$temp;
            
        }

        $this->logicWidget = $logicWidget;        
        
    }

    /**
     * 检查是否满足插件执行条件
     *
     * @param string $condition
     * @return void
     */
    private function checkCondition($condition)
    {
        if(empty($condition))return true;

        $conditionArr = $this->parseCondition($condition);

        foreach ($conditionArr as $con) {
            switch ($con['field']) {
                case 'caller':
                    if($this->container->caller!=$con['value'])return false;
                    break;
                
                default:
                    break;
            }
        }

        return true;
    }

    /**
     * 解析插件执行条件
     *
     * @param string $condition
     * @return array
     */
    private function parseCondition(string $condition)
    {
        $list = explode(',',$condition);

        $conditionArr = [];

        foreach ($list as $key => $value) {
            $express = explode('|',$value);
            $conditionArr[$key]=[
                'field'=>$express[0],
                'operator'=>$express[1],
                'value'=>$express[2]??null
            ];
        }

        return $conditionArr;
    }

    /**
     * 执行服务逻辑
     *
     * @param string $position
     * @param array ...$param
     * @return void
     */
    public function exec($position,&...$param)
    {
        $widgets = $this->logicWidget[$position]??[];
        foreach ($widgets as $widget) {
            if(!$this->checkCondition($widget['condition']))continue;
            $method = $widget['method'];
            switch ($widget['type']) {
                case 'method':
                    $res = service($widget['class'])->$method(...$param);
                    break;
                case 'widget':
                    $obj = new $widget['class']($this->container->context);
                    $res = $obj->$method(...$param);
                    break;
                default:
                    # code...
                    break;
            }

            if(!$res){
                return false;
            }
        }

        return true;
    }
}